﻿/*
 * modbusregs.cpp
 *
 *  Created on: 2019-4-13
 *      Author: work
 */

#include <dm/export.hpp>

#define DM_API_PROTOCOL DM_API_EXPORT

#include <dm/protocol/modbusregs.hpp>
#include <dm/bcd.hpp>
#include <dm/bits.hpp>
#include <dm/bytes.hpp>

#include <dm/protocol/modbusmap_status.hpp>
#include <dm/protocol/modbusmap_discrete.hpp>
#include <dm/protocol/modbusmap_measure.hpp>
#include <dm/protocol/modbusmap_cumulant.hpp>

namespace dm{
namespace protocol{

CModbusRegs::CSegment::CSegment( const dm::uint16& address,const dm::uint16& quantity,int dataLen ):
		m_address(address),m_quantity(quantity),m_len(dataLen),m_updated(false){
	m_data = new dm::uint8[m_len];
}

CModbusRegs::CSegment::~CSegment(){
	delete [] m_data;
}

bool CModbusRegs::CSegment::asBit( const dm::uint16& address )const{
	int offset = address;
	offset -= m_address;
	if( offset<0 || offset>=m_quantity )
		return false;

	return 0!=((m_data[offset/8]>>(offset%8))&0x01);
}

CModbusRegs::CModbusRegs():m_curFun(dm::protocol::CFrameModbusApdu::ReadCoilStatus),m_curSegIdx(-1){
}

CModbusRegs::~CModbusRegs(){
	for( int f=0;f<4;++f ){
		for( unsigned int i=0;i<m_segments[f].size();++i )
			delete m_segments[f][i];
	}
}

bool CModbusRegs::addSegment( const fun_t& fun,const dm::uint16& addr,const dm::uint16& quantity ){
	if( fun<1 || fun>4 )
		return false;
	if( fun==1||fun==2 )
		m_segments[fun-1].push_back(new CSegment(addr,quantity,dm::protocol::CFrameModbusApdu::getBytes(quantity)));
	else
		m_segments[fun-1].push_back(new CSegment(addr,quantity,quantity*2));
	return true;
}

void CModbusRegs::curReset(){
	for( int f=0;f<4;++f ){
		if( m_segments[f].size()>0 ){
			m_curFun = fun_t(f+1);
			m_curSegIdx = 0;
			return;
		}
	}

	m_curSegIdx = -1;
}

const CModbusRegs::CSegment* CModbusRegs::curSegment()const{
	if( m_curSegIdx==-1 )
		return 0;

	return m_segments[m_curFun-1][m_curSegIdx];
}

bool CModbusRegs::curNext(){
	if( m_curSegIdx==-1 )
		return false;

	++m_curSegIdx;
	while( m_curFun<=4 ){
		if( m_curSegIdx<int(m_segments[m_curFun-1].size()) )
			return true;
		m_curFun = fun_t(m_curFun+1);
		m_curSegIdx = 0;
	}

	m_curSegIdx = -1;
	return false;
}

void CModbusRegs::clearUpdate(){
	for( int f=0;f<4;++f ){
		for( unsigned int i=0;i<m_segments[f].size();++i )
			m_segments[f][i]->clearUpdated();
	}
}

bool CModbusRegs::update( const fun_t& fun,const dm::uint16& address,const dm::uint16& quantity,const dm::uint8* data,const int& len ){
	if( fun<1 || fun>4 )
		return false;
	for( unsigned int i=0;i<m_segments[fun-1].size();++i ){
		if( m_segments[fun-1][i]->address()==address ){
			if( m_segments[fun-1][i]->quantity()!=quantity )
				return false;
			if( m_segments[fun-1][i]->len()!=len )
				return false;
			for( int j=0;j<len;++j )
				m_segments[fun-1][i]->data()[j] = data[j];
			m_segments[fun-1][i]->setUpdated();
			return true;
		}
	}
	return false;
}

bool CModbusRegs::isUpdated()const{
	for( int f=0;f<4;++f ){
		for( unsigned int i=0;i<m_segments[f].size();++i ){
			if( m_segments[f][i]->updated()==false )
				return false;
		}
	}
	return true;
}

bool CModbusRegs::isUpdated( const fun_t& fun,const dm::uint16& address )const{
	if( fun<1 || fun>4 )
		return false;
	for( unsigned int i=0;i<m_segments[fun-1].size();++i ){
		if( m_segments[fun-1][i]->ifAddressed(address) )
			return m_segments[fun-1][i]->updated();
	}

	return false;
}

bool CModbusRegs::ifAddressed( const fun_t& fun,const dm::uint16& address,const dm::uint16& quantity )const{
	return addressSegment(fun,address,quantity)!=0;
}

dm::uint16 CModbusRegs::get( const fun_t& fun,const dm::uint16& address )const{
	if( fun<1 || fun>4 || address==0xFFFF )
		return 0;

	for( unsigned int i=0;i<m_segments[fun-1].size();++i ){
		const CSegment* segment = m_segments[fun-1][i];
		if( segment->ifAddressed(address) ){
			if( fun<=2 ){
				return segment->asBit(address)?0x0001:0x0000;
			}else{
				return toUint16(segment->data(address));
			}
		}
	}

	return 0;
}

dm::uint8 CModbusRegs::get01( const dm::uint16& address )const{
	return get(dm::protocol::CFrameModbusApdu::ReadCoilStatus,address);
}

dm::uint8 CModbusRegs::get02( const dm::uint16& address )const{
	return get(dm::protocol::CFrameModbusApdu::ReadInputStatus,address);
}

const dm::uint8* CModbusRegs::get03( const dm::uint16& address )const{
	for( unsigned int i=0;i<m_segments[2].size();++i ){
		if( m_segments[2][i]->ifAddressed(address) )
			return m_segments[2][i]->data(address);
	}

	return 0;
}

const dm::uint8* CModbusRegs::get04( const dm::uint16& address )const{
	for( unsigned int i=0;i<m_segments[3].size();++i ){
		if( m_segments[3][i]->ifAddressed(address) )
			return m_segments[3][i]->data(address);
	}

	return 0;
}

template<typename T,int b>
static T changeTo( const dm::uint8* p ){
	union{
		dm::uint16 w[b];
		T v;
	}all;

	for( int i=0;i<b;++i )
		all.w[i] = bytes2uint16(p[i*2+1],p[i*2+0]);
	return all.v;
}

template<typename T,int b>
static T changeToInv( const dm::uint8* p ){
	union{
		dm::uint16 w[b];
		T v;
	}all;

	for( int i=0;i<b;++i )
		all.w[b-1-i] = bytes2uint16(p[i*2+1],p[i*2]);
	return all.v;
}

template<typename T,int b>
static void changeFrom( const T& v,dm::uint8* p ){
	union{
		dm::uint16 w[b];
		T v;
	} all;
	all.v = v;
	for( int i=0;i<b;++i ){
		p[i*2+1] = lowByte_u16(all.w[i]);
		p[i*2] = higByte_u16(all.w[i]);
	}
}

template<typename T,int b>
static void changeFromInv( const T& v,dm::uint8* p ){
	union UReg{
		dm::uint16 w[b];
		T v;
	} all;

	all.v = v;

	for( int i=0;i<b;++i ){
		p[i*2+1] = lowByte_u16(all.w[b-1-i]);
		p[i*2] = higByte_u16(all.w[b-1-i]);
	}
}

dm::int16 CModbusRegs::toInt16( const dm::uint8* p ){
	return changeTo<dm::int16,1>(p);
}

dm::uint16 CModbusRegs::toUint16( const dm::uint8* p ){
	return changeTo<dm::uint16,1>(p);
}

dm::int32 CModbusRegs::toInt32( const dm::uint8* p ){
	return changeTo<dm::int32,2>(p);
}

dm::int32 CModbusRegs::toInt32Inv( const dm::uint8* p ){
	return changeToInv<dm::int32,2>(p);
}

dm::uint32 CModbusRegs::toUint32( const dm::uint8* p ){
	return changeTo<dm::uint32,2>(p);
}

dm::uint32 CModbusRegs::toUint32Inv( const dm::uint8* p ){
	return changeToInv<dm::uint32,2>(p);
}

dm::int64 CModbusRegs::toInt64( const dm::uint8* p ){
	return changeTo<dm::int64,4>(p);
}

dm::int64 CModbusRegs::toInt64Inv( const dm::uint8* p ){
	return changeToInv<dm::int64,4>(p);
}

dm::uint64 CModbusRegs::toUint64( const dm::uint8* p ){
	return changeTo<dm::uint64,4>(p);
}

dm::uint64 CModbusRegs::toUint64Inv( const dm::uint8* p ){
	return changeToInv<dm::uint64,4>(p);
}

dm::float32 CModbusRegs::toFloat32( const dm::uint8* p ){
	return changeTo<dm::float32,2>(p);
}

dm::float32 CModbusRegs::toFloat32Inv( const dm::uint8* p ){
	return changeToInv<dm::float32,2>(p);
}

dm::float64 CModbusRegs::toFloat64( const dm::uint8* p ){
	return changeTo<dm::float64,4>(p);
}

dm::float64 CModbusRegs::toFloat64Inv( const dm::uint8* p ){
	return changeToInv<dm::float64,4>(p);
}

dm::uint16 CModbusRegs::toBcdUint16( const dm::uint8* p ){
	return dm::fromBcd4(*p);
}

dm::uint32 CModbusRegs::toBcdUint32( const dm::uint8* p ){
	return dm::fromBcd8(toUint32(p));
}

dm::uint32 CModbusRegs::toBcdUint32Inv( const dm::uint8* p ){
	return dm::fromBcd8(toUint32Inv(p));
}

dm::uint64 CModbusRegs::toBcdUint64( const dm::uint8* p ){
	return dm::fromBcd16(toUint64(p));
}

dm::uint64 CModbusRegs::toBcdUint64Inv( const dm::uint8* p ){
	return dm::fromBcd16(toUint64Inv(p));
}

bool CModbusRegs::bitCheck( const dm::uint8* p,int bit ){
	return 0!=(toUint16(p)&dm::SetBits16[bit]);
}

void CModbusRegs::fromInt16( const dm::int16& v,dm::uint8* p ){
	changeFrom<dm::int16,1>(v,p);
}

void CModbusRegs::fromUint16( const dm::uint16& v,dm::uint8* p ){
	changeFrom<dm::uint16,1>(v,p);
}

void CModbusRegs::fromInt32( const dm::int32& v,dm::uint8* p ){
	changeFrom<dm::int32,2>(v,p);
}

void CModbusRegs::fromInt32Inv( const dm::int32& v,dm::uint8* p ){
	changeFromInv<dm::int32,2>(v,p);
}

void CModbusRegs::fromUint32( const dm::uint32& v,dm::uint8* p ){
	changeFrom<dm::uint32,2>(v,p);
}

void CModbusRegs::fromUint32Inv( const dm::uint32& v,dm::uint8* p ){
	changeFromInv<dm::uint32,2>(v,p);
}

void CModbusRegs::fromInt64( const dm::int64& v,dm::uint8* p ){
	changeFrom<dm::int64,4>(v,p);
}

void CModbusRegs::fromInt64Inv( const dm::int64& v,dm::uint8* p ){
	changeFromInv<dm::int64,4>(v,p);
}

void CModbusRegs::fromUint64( const dm::uint64& v,dm::uint8* p ){
	changeFrom<dm::uint64,4>(v,p);
}

void CModbusRegs::fromUint64Inv( const dm::uint64& v,dm::uint8* p ){
	changeFromInv<dm::uint64,4>(v,p);
}

void CModbusRegs::fromFloat32( const dm::float32& v,dm::uint8* p ){
	changeFrom<dm::float32,2>(v,p);
}

void CModbusRegs::fromFloat32Inv( const dm::float32& v,dm::uint8* p ){
	changeFromInv<dm::float32,2>(v,p);
}

void CModbusRegs::fromFloat64( const dm::float64& v,dm::uint8* p ){
	changeFrom<dm::float64,4>(v,p);
}

void CModbusRegs::fromFloat64Inv( const dm::float64& v,dm::uint8* p ){
	changeFromInv<dm::float64,4>(v,p);
}

void CModbusRegs::fromBcdUint16( const dm::uint16& v,dm::uint8* p ){
	fromUint16(dm::toBcd4(v),p);
}

void CModbusRegs::fromBcdUint32( const dm::uint32& v,dm::uint8* p ){
	fromUint32(dm::toBcd8(v),p);
}

void CModbusRegs::fromBcdUint32Inv( const dm::uint32& v,dm::uint8* p ){
	fromUint32Inv(dm::toBcd8(v),p);
}

void CModbusRegs::fromBcdUint64( const dm::uint64& v,dm::uint8* p ){
	fromUint64(dm::toBcd16(v),p);
}

void CModbusRegs::fromBcdUint64Inv( const dm::uint64& v,dm::uint8* p ){
	fromUint64Inv(dm::toBcd16(v),p);
}

void CModbusRegs::bitClear( const int& bit,dm::uint8* p ){
	changeFrom<dm::uint16,1>(changeTo<dm::uint16,2>(p)&dm::ClrBits16[bit],p);
}

void CModbusRegs::bitSet( const int& bit,dm::uint8* p ){
	changeFrom<dm::uint16,1>(changeTo<dm::uint16,2>(p)|dm::SetBits16[bit],p);
}

dm::uint16 CModbusRegs::combine( const int& bitOffset,const int& bitLen,const dm::uint16& v,dm::uint16 reg ){
	dm::uint16 mask = 0;
	for( int i=0;i<bitLen;++i )
		mask |= dm::SetBits16[bitOffset+i];

	dm::uint16 va = (v<<bitOffset) & mask;
	return (reg&(~mask))|va;
}

CModbusRegs::CSegment* CModbusRegs::addressSegment( const fun_t& fun,const dm::uint16& address,const dm::uint16& quantity ){
	if( fun<0 || fun>4 )
		return 0;
	for( unsigned int i=0;i<m_segments[fun-1].size();++i ){
		if( m_segments[fun-1][i]->ifAddressed(address,quantity) )
			return m_segments[fun-1][i];
	}

	return 0;
}

const CModbusRegs::CSegment* CModbusRegs::addressSegment( const fun_t& fun,const dm::uint16& address,const dm::uint16& quantity )const{
	if( fun<0 || fun>4 )
		return 0;
	for( unsigned int i=0;i<m_segments[fun-1].size();++i ){
		if( m_segments[fun-1][i]->ifAddressed(address,quantity) )
			return m_segments[fun-1][i];
	}

	return 0;
}

}
}
